package com.lagou.server;

import com.lagou.constant.Constant;
import com.lagou.model.Question;
import com.lagou.model.Store;
import com.lagou.model.User;
import com.lagou.model.UserMessage;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

/**

 * TODO: 编程实现服务器的主功能

 */
public class ServerView {
    /**
     * 使用合成复用原则
     */
    private final ServerInitClose sic;
    private final ServerManagerDao smd;
    private final List<User> userList;
    private final List<Question> questionList;
    /**
     * 自定义StringBuilder类型的引用变量用于描述日志
     */
    private final StringBuilder sb;

    /**
     * 通过构造方法实现成员变量的初始化
     *
     * @param sic          服务器初始化关闭类的实例
     * @param smd          服务器管理员操作类的实例
     * @param userList     学生用户集合
     * @param questionList 考题集合
     */
    public ServerView(ServerInitClose sic, ServerManagerDao smd, List<User> userList, List<Question> questionList) {
        this.sic = sic;
        this.smd = smd;
        this.userList = userList;
        this.questionList = questionList;
        this.sb = new StringBuilder();
    }

    /**
     * 自定义成员方法实现接收客户端发来的消息并处理
     */
    public void receive() throws IOException, ClassNotFoundException {
        while (true) {
            System.out.println("等待数据到来。。。");
            UserMessage tum = (UserMessage) sic.getOis().readObject();
            sb.append("服务端收到客户端[").append(sic.getS().getInetAddress()).append("]发来的");
            switch (tum.getType()) {
                case Constant.MANAGER_LOGIN:
                    sb.append("管理员登陆请求，")
                            .append("请求的数据为：")
                            .append(tum)
                            .append("调用 managerCheck() 方法")
                            .append("响应的结果为：{'result','");
                    managerCheck(tum);
                    break;
                case Constant.EXIT:
                    sb.append("客户端退出请求，")
                            .append("请求的数据为：")
                            .append(tum)
                            .append("调用 exit() 方法")
                            .append("响应的结果为：{'result','");
                    exit();
                    return;
                case Constant.ADD_USER:
                    sb.append("客户端添加学生用户请求，")
                            .append("请求的数据为：")
                            .append(tum)
                            .append("调用 addUser() 方法")
                            .append("响应的结果为：{'result','");
                    addUser(tum);
                    break;
                case Constant.DELETE_USER:
                    sb.append("客户端删除学生用户请求，")
                            .append("请求的数据为：")
                            .append(tum)
                            .append("调用 deleteUser() 方法")
                            .append("响应的结果为：{'result','");
                    deleteUser(tum);
                    break;
                case Constant.UPDATE_USER:
                    sb.append("客户端修改学生用户请求，")
                            .append("请求的数据为：")
                            .append(tum)
                            .append("调用 updateUser() 方法")
                            .append("响应的结果为：{'result','");
                    updateUser(tum);
                    break;
                case Constant.GET_USER:
                    sb.append("客户端查询学生用户请求，")
                            .append("请求的数据为：")
                            .append(tum)
                            .append("调用 getUser() 方法")
                            .append("响应的结果为：{'result','");
                    getUser(tum);
                    break;
                case Constant.ADD_QUESTION:
                    sb.append("客户端添加考题请求，")
                            .append("请求的数据为：")
                            .append(tum)
                            .append("调用 addQuestion() 方法")
                            .append("响应的结果为：{'result','");
                    addQuestion(tum);
                    break;
                case Constant.DELETE_QUESTION:
                    sb.append("客户端删除考题请求，")
                            .append("请求的数据为：")
                            .append(tum)
                            .append("调用 deleteQuestion() 方法")
                            .append("响应的结果为：{'result','");
                    deleteQuestion(tum);
                    break;
                case Constant.UPDATE_QUESTION:
                    sb.append("客户端修改考题请求，")
                            .append("请求的数据为：")
                            .append(tum)
                            .append("调用 updateQuestion() 方法")
                            .append("响应的结果为：{'result','");
                    updateQuestion(tum);
                    break;
                case Constant.GET_QUESTION:
                    sb.append("客户端查询考题请求，")
                            .append("请求的数据为：")
                            .append(tum)
                            .append("调用 getQuestion() 方法")
                            .append("响应的结果为：{'result','");
                    getQuestion(tum);
                    break;
                case Constant.IMPORT_QUESTION:
                    sb.append("客户端导入考题请求，")
                            .append("请求的数据为：")
                            .append(tum)
                            .append("调用 importQuestion() 方法")
                            .append("响应的结果为：{'result','");
                    importQuestion(tum);
                    break;
                case Constant.USER_LOGIN:
                    sb.append("客户端学员登陆请求，")
                            .append("请求的数据为：")
                            .append(tum)
                            .append("调用 userCheck() 方法")
                            .append("响应的结果为：{'result','");
                    userCheck(tum);
                    break;
                case Constant.CHANGE_PASS:
                    sb.append("客户端修改学生用户密码请求，")
                            .append("请求的数据为：")
                            .append(tum)
                            .append("调用 changePass() 方法")
                            .append("响应的结果为：{'result','");
                    changePass(tum);
                    break;
                case Constant.START_EXAM:
                    sb.append("客户端开始考试请求，")
                            .append("请求的数据为：")
                            .append(tum)
                            .append("调用 startExam() 方法")
                            .append("响应的结果为：{'result','");
                    startExam(tum);
                    break;
                case Constant.END_EXAM:
                    sb.append("客户端结束考试请求，")
                            .append("请求的数据为：")
                            .append(tum)
                            .append("调用 endExam() 方法")
                            .append("响应的结果为：{'result','");
                    endExam(tum);
                    break;
                case Constant.GET_STORE:
                    sb.append("客户端查询成绩请求，")
                            .append("请求的数据为：")
                            .append(tum)
                            .append("调用 getStore() 方法")
                            .append("响应的结果为：{'result','");
                    getStore(tum);
                    break;
                case Constant.EXPORT_STORE:
                    sb.append("客户端导出成绩请求，")
                            .append("请求的数据为：")
                            .append(tum)
                            .append("调用 exportStore() 方法")
                            .append("响应的结果为：{'result','");
                    exportStore(tum);
                    break;
                default:
                    sb.append("客户端请求消息类型错误，")
                            .append("请求的数据为：")
                            .append(tum)
                            .append("调用 messageError() 方法")
                            .append("响应的结果为：{'result','");
                    messageError(tum);
            }
            System.out.println(sb.toString());
            // 清空本次日志
            sb.delete(0, sb.length());
        }
    }

    /**
     * 自定义成员方法实现服务器导出成绩的功能
     * @param tum 消息实体
     */
    private void exportStore(UserMessage tum) throws IOException {
        StringBuilder storeSb = new StringBuilder("用户名：");
        List<Store> storeList;
        // 判断集合中是否存在要查找的学员信息，若存在则直接返回该学员的成绩信息
        for (User user : userList) {
            if (user.getUserName().equalsIgnoreCase(tum.getUser().getUserName())) {
                storeSb.append(tum.getUser().getUserName());
                storeSb.append("\n");
                tum.setType(Constant.SUCCESS);
                sb.append(Constant.SUCCESS).append("'}");
                storeList = user.getStoreList();
                if (null == storeList) {
                    storeSb.append("目前还没有历史成绩！");
                } else {
                    for (int i = 0; i < storeList.size(); i++) {
                        storeSb.append("第").append(i + 1).append("次考试，").append(storeList.get(i).show()).append("\n");
                    }
                }
                // 添加历史成绩数据
                tum.setStoreData(storeSb.toString().toCharArray());
                sic.getOos().writeObject(tum);
                return;
            }
        }
        // 若不存在导出失败
        tum.setType(Constant.FAIL);
        sb.append(Constant.FAIL).append("'}");
        sic.getOos().writeObject(tum);
    }

    /**
     * 自定义成员方法实现服务器查询成绩的功能
     * @param tum 消息实体
     */
    private void getStore(UserMessage tum) throws IOException {
        // 成绩都在用户信息里，所以直接调用查询用户的方法即可
        getUser(tum);
    }

    /**
     * 自定义成员方法实现服务器结束考试时计算并发送成绩的功能
     * @param tum 消息实体
     */
    private void endExam(UserMessage tum) throws IOException {
        List<Question> testPaper = tum.getTestPaper();
        List<String> answerList = tum.getAnswerList();
        // 计算每道题的应得分数
        BigDecimal avg = BigDecimal.valueOf(100);
        avg = avg.divide(BigDecimal.valueOf(Constant.EXAM_NUM), RoundingMode.HALF_UP);
        // 记录成绩
        BigDecimal totalScore = new BigDecimal(0);
        // 计算成绩
        for (int i = 0; i < testPaper.size(); i++) {
            // 如果试卷中的这道题答对了就得分
            if (testPaper.get(i).getAnswer().equals(answerList.get(i))) {
                totalScore = totalScore.add(avg);
            }
        }
        // 初始化相关成绩信息
        Store store = new Store(new Date(), totalScore);
        tum.setType(Constant.SUCCESS);
        sb.append(Constant.SUCCESS).append("'}");
        // 设置该学生用户成绩
        List<Store> storeList;
        // 判断集合中是否存在要查找的学员信息，若存在则更新历史成绩
        tum.getUser().setStore(store);
        for (User user : userList) {
            if (user.getUserName().equalsIgnoreCase(tum.getUser().getUserName())) {
                // 添加到该学生的历史成绩记录里
                storeList = user.getStoreList();
                // 如果该同学没有历史成绩，则初始化一个成绩记录集合
                if (null == storeList) {
                    storeList = new LinkedList<>();
                }
                // 添加成绩
                storeList.add(store);
                user.setStoreList(storeList);
            }
        }
        sic.getOos().writeObject(tum);
    }

    /**
     * 自定义成员方法实现服务器开始考试时发送试题的功能
     *
     * @param tum 消息实体
     */
    private void startExam(UserMessage tum) throws IOException {
        List<Question> questions = new ArrayList<>(questionList.size());
        questions.addAll(questionList);
        // 先打乱试题顺序
        Collections.shuffle(questions);
        // 获取一个范围在 0-(试题数减10) 的随机数，实现随机抽题
        Random random = new Random();
        // 再调用 Stream 流的方法从随机位置开始抽10道题
        List<Question> testPaper = questions.stream().skip(random.nextInt(questions.size() - Constant.EXAM_NUM)).limit(Constant.EXAM_NUM).collect(Collectors.toList());
        if (Constant.EXAM_NUM == testPaper.size()) {
            tum.setType(Constant.SUCCESS);
            sb.append(Constant.SUCCESS).append("'}");
            tum.setTestPaper(testPaper);
        } else {
            tum.setType(Constant.FAIL);
            sb.append(Constant.FAIL).append("'}");
        }
        sic.getOos().writeObject(tum);
    }

    /**
     * 自定义成员方法实现服务器处理错误消息的功能
     *
     * @param tum 消息实体
     */
    private void messageError(UserMessage tum) throws IOException {
        sb.append(Constant.MESSAGE_ERROR).append("'}");
        tum.setType(Constant.MESSAGE_ERROR);
        sb.append(Constant.MESSAGE_ERROR).append("'}");
        sic.getOos().writeObject(tum);
    }

    /**
     * 自定义成员方法实现服务器修改当前登录学员账户密码的功能
     *
     * @param tum 消息实体
     */
    private void changePass(UserMessage tum) throws IOException {
        // 因为这里修改密码所做操作跟修改学生账户信息所做操作一样，
        // 所以直接调用 updateUser()
        updateUser(tum);
    }

    /**
     * 自定义成员方法实现服务器导入考题的功能
     *
     * @param tum 消息实体
     */
    private void importQuestion(UserMessage tum) throws IOException {
        if (null != tum.getTestPaper() && !tum.getTestPaper().isEmpty()) {
            List<Question> testPaper = tum.getTestPaper();
            System.out.println("发送来的试题：" + testPaper);
            questionList.addAll(testPaper);
            tum.setType(Constant.SUCCESS);
            sb.append(Constant.SUCCESS).append("'}");
        } else {
            tum.setType(Constant.FAIL);
            sb.append(Constant.FAIL).append("'}");
        }
        sic.getOos().writeObject(tum);
    }

    /**
     * 自定义成员方法实现服务器增加考题的功能
     *
     * @param tum 消息实体
     */
    private void addQuestion(UserMessage tum) throws IOException {
        // 判断集合中是否存在要添加的考题信息，若存在则添加失败
        for (Question question : questionList) {
            if (question.getTitle().equalsIgnoreCase(tum.getQuestion().getTitle())) {
                tum.setType(Constant.FAIL);
                sb.append(Constant.FAIL).append("'}");
                sic.getOos().writeObject(tum);
                return;
            }
        }
        // 若不存在调用集合的add方法添加即可
        questionList.add(tum.getQuestion());
        tum.setType(Constant.SUCCESS);
        sb.append(Constant.SUCCESS).append("'}");
        sic.getOos().writeObject(tum);
    }

    /**
     * 自定义成员方法实现服务器删除考题的功能
     *
     * @param tum 消息实体
     */
    private void deleteQuestion(UserMessage tum) throws IOException {
        // 判断集合中是否存在要删除的学员信息，若存在则调用集合迭代器的remove方法即可
        Iterator<Question> iterator = questionList.iterator();
        while (iterator.hasNext()) {
            Question question = iterator.next();
            if (question.getTitle().equalsIgnoreCase(tum.getQuestion().getTitle())) {
                iterator.remove();
                tum.setType(Constant.SUCCESS);
                sb.append(Constant.SUCCESS).append("'}");
                sic.getOos().writeObject(tum);
                return;
            }
        }
        // 若不存在则删除失败
        tum.setType(Constant.FAIL);
        sb.append(Constant.FAIL).append("'}");
        sic.getOos().writeObject(tum);
    }

    /**
     * 自定义成员方法实现服务器修改的功能
     *
     * @param tum 消息实体
     */
    private void updateQuestion(UserMessage tum) throws IOException {
        // 判断集合中是否存在要修改的学员信息，若存在则直接修改
        for (Question question : questionList) {
            if (question.getTitle().equalsIgnoreCase(tum.getQuestion().getTitle())) {
                question.setAnswerA(tum.getQuestion().getAnswerA());
                question.setAnswerB(tum.getQuestion().getAnswerB());
                question.setAnswerC(tum.getQuestion().getAnswerC());
                question.setAnswerD(tum.getQuestion().getAnswerD());
                question.setAnswer(tum.getQuestion().getAnswer());
                tum.setType(Constant.SUCCESS);
                sb.append(Constant.SUCCESS).append("'}");
                sic.getOos().writeObject(tum);
                return;
            }
        }
        // 若不存在修改失败
        tum.setType(Constant.FAIL);
        sb.append(Constant.FAIL).append("'}");
        sic.getOos().writeObject(tum);
    }

    /**
     * 自定义成员方法实现服务器查询考题的功能
     *
     * @param tum 消息实体
     */
    private void getQuestion(UserMessage tum) throws IOException {
        // 判断集合中是否存在要查找的学员信息，若存在则直接返回该学员信息
        for (Question question : questionList) {
            if (question.getTitle().equalsIgnoreCase(tum.getQuestion().getTitle())) {
                tum.setType(Constant.SUCCESS);
                sb.append(Constant.SUCCESS).append("'}");
                tum.setQuestion(question);
                // 重置，让其不从缓存中拿
                sic.getOos().reset();
                sic.getOos().writeObject(tum);
                return;
            }
        }
        // 若不存在查询失败
        tum.setType(Constant.FAIL);
        sb.append(Constant.FAIL).append("'}");
        sic.getOos().writeObject(tum);
    }

    /**
     * 自定义成员方法实现服务器增加学员账户的功能
     *
     * @param tum 消息实体
     */
    private void addUser(UserMessage tum) throws IOException {
        // 判断集合中是否存在要添加的学员信息，若存在则添加失败
        for (User user : userList) {
            if (user.getUserName().equalsIgnoreCase(tum.getUser().getUserName())) {
                tum.setType(Constant.FAIL);
                sb.append(Constant.FAIL).append("'}");
                sic.getOos().writeObject(tum);
                return;
            }
        }
        // 若不存在调用集合的add方法添加即可
        tum.getUser().setPassword("666666");
        userList.add(tum.getUser());
        tum.setType(Constant.SUCCESS);
        sb.append(Constant.SUCCESS).append("'}");
        sic.getOos().writeObject(tum);
    }

    /**
     * 自定义成员方法实现服务器删除学员账户的功能
     *
     * @param tum 消息实体
     */
    private void deleteUser(UserMessage tum) throws IOException {
        // 判断集合中是否存在要删除的学员信息，若存在则调用集合迭代器的remove方法即可
        Iterator<User> iterator = userList.iterator();
        while (iterator.hasNext()) {
            User user = iterator.next();
            if (user.getUserName().equalsIgnoreCase(tum.getUser().getUserName())) {
                iterator.remove();
                tum.setType(Constant.SUCCESS);
                sb.append(Constant.SUCCESS).append("'}");
                sic.getOos().writeObject(tum);
                return;
            }
        }
        // 若不存在则删除失败
        tum.setType(Constant.FAIL);
        sb.append(Constant.FAIL).append("'}");
        sic.getOos().writeObject(tum);
    }

    /**
     * 自定义成员方法实现服务器修改学员账户的功能
     *
     * @param tum 消息实体
     */
    private void updateUser(UserMessage tum) throws IOException {
        // 判断集合中是否存在要修改的学员信息，若存在则直接修改
        for (User user : userList) {
            if (user.getUserName().equalsIgnoreCase(tum.getUser().getUserName())) {
                user.setPassword(tum.getUser().getPassword());
                tum.setType(Constant.SUCCESS);
                sb.append(Constant.SUCCESS).append("'}");
                sic.getOos().writeObject(tum);
                return;
            }
        }
        // 若不存在修改失败
        tum.setType(Constant.FAIL);
        sb.append(Constant.FAIL).append("'}");
        sic.getOos().writeObject(tum);
    }

    /**
     * 自定义成员方法实现服务器查询学员账户的功能
     *
     * @param tum 消息实体
     */
    private void getUser(UserMessage tum) throws IOException {
        // 判断集合中是否存在要查找的学员信息，若存在则直接返回该学员信息
        for (User user : userList) {
            if (user.getUserName().equalsIgnoreCase(tum.getUser().getUserName())) {
                tum.setType(Constant.SUCCESS);
                sb.append(Constant.SUCCESS).append("'}");
                tum.setUser(user);
                // 重置，让其不从缓存中拿
                sic.getOos().reset();
                sic.getOos().writeObject(tum);
                return;
            }
        }
        // 若不存在查询失败
        tum.setType(Constant.FAIL);
        sb.append(Constant.FAIL).append("'}");
        sic.getOos().writeObject(tum);
    }

    /**
     * 自定义成员方法实现服务器的退出功能
     */
    private void exit() {
        System.out.println("客户端已退出，服务器也退出了！");
    }

    /**
     * 自定义成员方法实现学员账户密码校验的功能
     *
     * @param tum 消息实体
     */
    private void userCheck(UserMessage tum) throws IOException {
        // 判断集合中是否存在该用户名和密码的学员信息，若存在则直接返回该学员信息
        for (User user : userList) {
            if (user.getUserName().equalsIgnoreCase(tum.getUser().getUserName()) && user.getPassword().equals(tum.getUser().getPassword())) {
                tum.setType(Constant.SUCCESS);
                sb.append(Constant.SUCCESS).append("'}");
                sic.getOos().writeObject(tum);
                return;
            }
        }
        // 若不存在查询失败
        tum.setType(Constant.FAIL);
        sb.append(Constant.FAIL).append("'}");
        sic.getOos().writeObject(tum);
    }

    /**
     * 自定义成员方法实现管理员账户密码校验的功能
     *
     * @param tum 消息实体
     */
    private void managerCheck(UserMessage tum) throws IOException {
        // 调用方法实现管理员账户和密码信息的校验
        if (smd.serverManagerCheck(tum.getUser())) {
            tum.setType(Constant.SUCCESS);
            sb.append(Constant.SUCCESS).append("'}");
        } else {
            tum.setType(Constant.FAIL);
            sb.append(Constant.FAIL).append("'}");
        }
        // 将校验结果发送给客户端
        sic.getOos().writeObject(tum);
    }
}
